package com.googlecode.droidcare.common;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

public class ScriptEngine {

	private Scriptable scriptableScope;
	
	private static final String DEFAULT_SOURCE_NAME = "inner_source";
	private static final int OPTIMIZATION_LEVEL = -1;
	
	public ScriptEngine() {
		try {
			Context scriptContext = enterContext();
			scriptableScope = scriptContext.initStandardObjects();
			ScriptableObject.putProperty(scriptableScope, "Context", AppContext.getInstance());
		} finally {
			leaveContext();
		}
	}
	
	public Context enterContext() {
		Context ctx = Context.enter();
		ctx.setOptimizationLevel(OPTIMIZATION_LEVEL);
		return ctx;
	}
	
	public void leaveContext() {
		Context.exit();
	}
	
	public Object eval(String script) {
		try {
			return enterContext().evaluateString(scriptableScope, script, DEFAULT_SOURCE_NAME, 1, null);
		} finally {
			leaveContext();
		}
	}
	
	public Object eval(File file) {
		Object result = null;
		try {
			result = enterContext().evaluateReader(scriptableScope, new FileReader(file), file.getAbsolutePath(), 1, null);
		} catch (FileNotFoundException e) {
			e.printStackTrace(); //TODO use exception handler
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			leaveContext();
		}
		return result;
	}
	
	public Object eval(InputStream inputStream) {
		Object result = null;
		try {
			result = enterContext().evaluateReader(scriptableScope, new InputStreamReader(inputStream), inputStream.toString(), 1, null);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			leaveContext();
		}
		return result;
	}
	
	public Object callFunction(String functionName, Object... args) {
		Function function = (Function)scriptableScope.get(functionName, scriptableScope);
		return callFunction(function, args);
	}
	
	public Object callFunction(Function function, Object... args) {
		try {
			return function.call(enterContext(), scriptableScope, scriptableScope, args);
		} finally {
			leaveContext();
		}
	}

}
